package io.hellsinger.vortex.ui.screen.storage.transaction

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.os.Bundle
import android.os.Parcelable
import android.view.Gravity
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.WindowInsets
import android.widget.FrameLayout
import android.widget.FrameLayout.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout.LayoutParams.WRAP_CONTENT
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsCompat.Type
import androidx.core.view.updatePadding
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.hellsinger.navigation.screen.ScreenController
import io.hellsinger.theme.vortex.VortexSettings
import io.hellsinger.viewmodel.BitViewModel.UiStateUpdateCollector
import io.hellsinger.vortex.R
import io.hellsinger.vortex.bit.Bits.Companion.hasFlag
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.di.createViewModel
import io.hellsinger.vortex.navigation.findService
import io.hellsinger.vortex.navigation.globalUiProvider
import io.hellsinger.vortex.service.VortexServiceBinder
import io.hellsinger.vortex.ui.component.LoadingView
import io.hellsinger.vortex.ui.component.adapter.ItemAdapter
import io.hellsinger.vortex.ui.component.adapter.Items
import io.hellsinger.vortex.ui.component.adapter.UnsupportedViewTypeException
import io.hellsinger.vortex.ui.component.adapter.listener.ItemListener
import io.hellsinger.vortex.ui.component.drawable.RoundedCornerDrawable
import io.hellsinger.vortex.ui.component.item.menu.MenuAction
import io.hellsinger.vortex.ui.component.item.menu.MenuActionListener
import io.hellsinger.vortex.ui.component.item.title.TitleItemView
import io.hellsinger.vortex.ui.component.item.twoline.TwoLineItemView
import io.hellsinger.vortex.ui.component.navigation.NavigationBarView
import io.hellsinger.vortex.ui.component.observeInsets
import io.hellsinger.vortex.ui.dsl.frame
import io.hellsinger.vortex.ui.dsl.frameParams
import io.hellsinger.vortex.ui.dsl.loading
import io.hellsinger.vortex.ui.dsl.recycler
import io.hellsinger.vortex.ui.icon.Icons
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionViewModel.Companion.UPDATE_INFO
import io.hellsinger.vortex.ui.screen.storage.transaction.StorageTransactionViewModel.Companion.UPDATE_LOADING
import kotlinx.coroutines.flow.map
import kotlinx.parcelize.Parcelize

class StorageTransactionScreenController(
    private val context: Context,
) : ScreenController<StorageTransactionScreenController.Args>(),
    View.OnApplyWindowInsetsListener,
    UiStateUpdateCollector<UiState>,
    ItemListener,
    MenuActionListener {
    private var viewModel: StorageTransactionViewModel? = null

    private var animator: ValueAnimator? = null
    private val adapter =
        ItemAdapter(
            listener = this,
            viewProvider = { parent, viewType ->
                when (viewType) {
                    Items.TWO_LINE ->
                        TwoLineItemView(parent.context).apply {
                            setTitleColor(VortexSettings.colors { key_storage_transaction_two_line_title_color })
                            setDescriptionColor(VortexSettings.colors { key_storage_transaction_two_line_description_color })
                            setRippleColor(VortexSettings.colors { key_storage_transaction_two_line_ripple_color })
                            setBackgroundColor(VortexSettings.colors { key_storage_transaction_two_line_surface_color })
                        }

                    Items.TITLE ->
                        TitleItemView(parent.context).apply {
                            setTitleColor(VortexSettings.colors { key_storage_transaction_title_color })
                        }

                    else -> throw UnsupportedViewTypeException(viewType)
                }
            },
        )
    private var root: FrameLayout? = null
    private var loading: LoadingView? = null
    private var recycler: RecyclerView? = null

    private var bar: NavigationBarView? = globalUiProvider(context)?.bar

    override fun onBackActivatedImpl(): Boolean = false

    override fun onCreateViewImpl(context: Context): View {
        root =
            frame(context) {
                background =
                    RoundedCornerDrawable(VortexSettings.colors { key_storage_transaction_background_color })
                recycler =
                    recycler {
                        layoutManager = LinearLayoutManager(context)
                        adapter = this@StorageTransactionScreenController.adapter
                        frameParams(MATCH_PARENT, MATCH_PARENT)
                        observeInsets(this@StorageTransactionScreenController)
                        clipToPadding = false
                    }
                loading =
                    loading {
                        frameParams(MATCH_PARENT, WRAP_CONTENT) {
                            visibility = GONE
                            gravity = Gravity.CENTER
                        }
                        observeInsets(this@StorageTransactionScreenController)
                    }
            }
        return root!!
    }

    override fun onPrepareImpl() {
        if (viewModel == null) viewModel = context.createViewModel()

        viewModel?.collectUiState(this)
        viewModel?.buildChanges(args = args ?: return)

        viewModel?.collectServiceState(
            context.findService<VortexServiceBinder?>().map { binder -> binder?.storage },
        )

        bar?.setTitleWithAnimation("Summary")
        bar?.registerListener(this)
        bar?.replaceItems(
            listOf(
                MenuAction(
                    R.id.file_manager_transaction_apply,
                    "Apply",
                    Icons.Rounded.Check,
                ),
                MenuAction(
                    R.id.file_manager_transaction_schedule,
                    "Schedule",
                    Icons.Rounded.Schedule,
                ),
            ),
        )
    }

    override suspend fun emit(
        event: Int,
        state: UiState,
    ) {
        if (event hasFlag UPDATE_INFO) {
            adapter.replace(state.items)
            navigateRecycler()
        }
        if (event hasFlag UPDATE_LOADING) {
            navigateLoading()
        }
    }

    override fun onFocusImpl() {
    }

    override fun onHideImpl() {
        bar?.unregisterListener(this)
        viewModel?.cancelCollectUiState()
        viewModel?.cancelServiceCollecting()
    }

    override fun onApplyWindowInsets(
        v: View,
        insets: WindowInsets,
    ): WindowInsets {
        val compat = WindowInsetsCompat.toWindowInsetsCompat(insets)
        val statusBarInsets = compat.getInsets(Type.statusBars())
        val navigationBarInsets = compat.getInsets(Type.navigationBars())
        v.updatePadding(
            top = statusBarInsets.top,
            bottom = navigationBarInsets.bottom,
        )
        return insets
    }

    override fun onDestroyImpl() {
        viewModel?.onDestroy()
        viewModel = null
    }

    override fun onItemClick(
        view: View,
        position: Int,
    ) {
    }

    override fun onLongItemClick(
        view: View,
        position: Int,
    ): Boolean = false

    override fun onSaveImpl(
        buffer: Bundle,
        prefix: String,
    ): Boolean = false

    override fun onRestoreImpl(
        buffer: Bundle,
        prefix: String,
    ): Boolean = false

    override fun onMenuActionCall(id: Int): Boolean {
        when (id) {
            R.id.file_manager_transaction_apply -> {
                viewModel?.confirm(args ?: return true) {
                    router?.popTo(route = "storage_main_screen")
                }
                return true
            }

            R.id.file_manager_transaction_schedule -> {
            }
        }
        return false
    }

    private fun navigateLoading() {
        if (loading?.visibility == VISIBLE) return
        animator?.cancel()
        createSwitchAnimator()

        animator?.addUpdateListener { anim ->
            if (recycler?.visibility == VISIBLE) recycler?.alpha = 1F - anim.animatedFraction
            loading?.alpha = anim.animatedFraction
        }

        animator?.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationCancel(animation: Animator) {
                    loading?.alpha = 1F
                    loading?.visibility = VISIBLE
                    recycler?.visibility = GONE
                    animator = null
                }

                override fun onAnimationStart(animation: Animator) {
                    loading?.visibility = GONE
                }

                override fun onAnimationEnd(animation: Animator) {
                    loading?.alpha = 1F
                    loading?.visibility = VISIBLE
                    recycler?.visibility = GONE
                    animator = null
                }
            },
        )

        animator?.start()
    }

    private fun navigateRecycler() {
        if (recycler?.visibility == VISIBLE) return
        animator?.cancel()
        createSwitchAnimator()

        animator?.addUpdateListener { anim ->
            if (loading?.visibility == VISIBLE) loading?.alpha = 1F - anim.animatedFraction
            recycler?.alpha = anim.animatedFraction
        }

        animator?.addListener(
            object : AnimatorListenerAdapter() {
                override fun onAnimationCancel(animation: Animator) {
                    recycler?.alpha = 1F
                    recycler?.visibility = VISIBLE
                    loading?.visibility = GONE
                    animator = null
                }

                override fun onAnimationStart(animation: Animator) {
                    recycler?.visibility = GONE
                }

                override fun onAnimationEnd(animation: Animator) {
                    recycler?.alpha = 1F
                    recycler?.visibility = VISIBLE
                    loading?.visibility = GONE
                    animator = null
                }
            },
        )

        animator?.start()
    }

    private fun createSwitchAnimator(duration: Long = 150L): ValueAnimator {
        if (animator != null) return animator!!

        val animator = ValueAnimator.ofFloat(0F, 1F)
        animator.duration = duration
        this.animator = animator
        return animator
    }

    sealed interface Args : Parcelable {
        val type: Int

        fun <A : Args> cast() = this as A

        @Parcelize
        data class Modification(
            val items: List<PathItem>,
            override val type: Int,
        ) : Args

        @Parcelize
        data class Transition(
            val items: List<PathItem>,
            val dest: PathItem,
            override val type: Int,
        ) : Args

        companion object {
            const val LOAD_TYPE_DELETE = 0
            const val LOAD_TYPE_CREATE = 1
            const val LOAD_TYPE_RENAME = 2
            const val LOAD_TYPE_COPY = 3
            const val LOAD_TYPE_MOVE = 4
            const val LOAD_TYPE_SWAP = 5
        }
    }
}
